1   /*
2    * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.nio.ch;
27  
28  import java.io.*;
29  import java.lang.ref.*;
30  import java.net.*;
31  import java.nio.*;
32  import java.nio.channels.*;
33  import java.security.AccessController;
34  import java.security.PrivilegedExceptionAction;
35  import java.util.*;
36  
37  
38  // Make a socket channel look like a socket.
39  //
40  // The only aspects of java.net.Socket-hood that we don't attempt to emulate
41  // here are the interrupted-I/O exceptions (which our Solaris implementations
42  // attempt to support) and the sending of urgent data.  Otherwise an adapted
43  // socket should look enough like a real java.net.Socket to fool most of the
44  // developers most of the time, right down to the exception message strings.
45  //
46  // The methods in this class are defined in exactly the same order as in
47  // java.net.Socket so as to simplify tracking future changes to that class.
48  //
49  
50  public class SocketAdaptor
51      extends Socket
52  {
53  
54      // The channel being adapted
55      private final SocketChannelImpl sc;
56  
57      // Timeout "option" value for reads
58      private volatile int timeout = 0;
59  
60      // ## super will create a useless impl
61      private SocketAdaptor(SocketChannelImpl sc) {
62          this.sc = sc;
63      }
64  
65      public static Socket create(SocketChannelImpl sc) {
66          return new SocketAdaptor(sc);
67      }
68  
69      public SocketChannel getChannel() {
70          return sc;
71      }
72  
73      // Override this method just to protect against changes in the superclass
74      //
75      public void connect(SocketAddress remote) throws IOException {
76          connect(remote, 0);
77      }
78  
79      public void connect(SocketAddress remote, int timeout) throws IOException {
80          if (remote == null)
81              throw new IllegalArgumentException("connect: The address can't be null");
82          if (timeout < 0)
83              throw new IllegalArgumentException("connect: timeout can't be negative");
84  
85          synchronized (sc.blockingLock()) {
86              if (!sc.isBlocking())
87                  throw new IllegalBlockingModeException();
88  
89              try {
90  
91                  if (timeout == 0) {
92                      sc.connect(remote);
93                      return;
94                  }
95  
96                  // Implement timeout with a selector
97                  SelectionKey sk = null;
98                  Selector sel = null;
99                  sc.configureBlocking(false);
100                 try {
101                     if (sc.connect(remote))
102                         return;
103                     sel = Util.getTemporarySelector(sc);
104                     sk = sc.register(sel, SelectionKey.OP_CONNECT);
105                     long to = timeout;
106                     for (;;) {
107                         if (!sc.isOpen())
108                             throw new ClosedChannelException();
109                         long st = System.currentTimeMillis();
110                         int ns = sel.select(to);
111                         if (ns > 0 &&
112                             sk.isConnectable() && sc.finishConnect())
113                             break;
114                         sel.selectedKeys().remove(sk);
115                         to -= System.currentTimeMillis() - st;
116                         if (to <= 0) {
117                             try {
118                                 sc.close();
119                             } catch (IOException x) { }
120                             throw new SocketTimeoutException();
121                         }
122                     }
123                 } finally {
124                     if (sk != null)
125                         sk.cancel();
126                     if (sc.isOpen())
127                         sc.configureBlocking(true);
128                     if (sel != null)
129                         Util.releaseTemporarySelector(sel);
130                 }
131 
132             } catch (Exception x) {
133                 Net.translateException(x, true);
134             }
135         }
136 
137     }
138 
139     public void bind(SocketAddress local) throws IOException {
140         try {
141             sc.bind(local);
142         } catch (Exception x) {
143             Net.translateException(x);
144         }
145     }
146 
147     public InetAddress getInetAddress() {
148         SocketAddress remote = sc.remoteAddress();
149         if (remote == null) {
150             return null;
151         } else {
152             return ((InetSocketAddress)remote).getAddress();
153         }
154     }
155 
156     public InetAddress getLocalAddress() {
157         if (sc.isOpen()) {
158             SocketAddress local = sc.localAddress();
159             if (local != null)
160                 return ((InetSocketAddress)local).getAddress();
161         }
162         return new InetSocketAddress(0).getAddress();
163     }
164 
165     public int getPort() {
166         SocketAddress remote = sc.remoteAddress();
167         if (remote == null) {
168             return 0;
169         } else {
170             return ((InetSocketAddress)remote).getPort();
171         }
172     }
173 
174     public int getLocalPort() {
175         SocketAddress local = sc.localAddress();
176         if (local == null) {
177             return -1;
178         } else {
179             return ((InetSocketAddress)local).getPort();
180         }
181     }
182 
183     private class SocketInputStream
184         extends ChannelInputStream
185     {
186         private SocketInputStream() {
187             super(sc);
188         }
189 
190         protected int read(ByteBuffer bb)
191             throws IOException
192         {
193             synchronized (sc.blockingLock()) {
194                 if (!sc.isBlocking())
195                     throw new IllegalBlockingModeException();
196                 if (timeout == 0)
197                     return sc.read(bb);
198 
199                 // Implement timeout with a selector
200                 SelectionKey sk = null;
201                 Selector sel = null;
202                 sc.configureBlocking(false);
203                 try {
204                     int n;
205                     if ((n = sc.read(bb)) != 0)
206                         return n;
207                     sel = Util.getTemporarySelector(sc);
208                     sk = sc.register(sel, SelectionKey.OP_READ);
209                     long to = timeout;
210                     for (;;) {
211                         if (!sc.isOpen())
212                             throw new ClosedChannelException();
213                         long st = System.currentTimeMillis();
214                         int ns = sel.select(to);
215                         if (ns > 0 && sk.isReadable()) {
216                             if ((n = sc.read(bb)) != 0)
217                                 return n;
218                         }
219                         sel.selectedKeys().remove(sk);
220                         to -= System.currentTimeMillis() - st;
221                         if (to <= 0)
222                             throw new SocketTimeoutException();
223                     }
224                 } finally {
225                     if (sk != null)
226                         sk.cancel();
227                     if (sc.isOpen())
228                         sc.configureBlocking(true);
229                     if (sel != null)
230                         Util.releaseTemporarySelector(sel);
231                 }
232 
233             }
234         }
235     }
236 
237     private InputStream socketInputStream = null;
238 
239     public InputStream getInputStream() throws IOException {
240         if (!sc.isOpen())
241             throw new SocketException("Socket is closed");
242         if (!sc.isConnected())
243             throw new SocketException("Socket is not connected");
244         if (!sc.isInputOpen())
245             throw new SocketException("Socket input is shutdown");
246         if (socketInputStream == null) {
247             try {
248                 socketInputStream = AccessController.doPrivileged(
249                     new PrivilegedExceptionAction<InputStream>() {
250                         public InputStream run() throws IOException {
251                             return new SocketInputStream();
252                         }
253                     });
254             } catch (java.security.PrivilegedActionException e) {
255                 throw (IOException)e.getException();
256             }
257         }
258         return socketInputStream;
259     }
260 
261     public OutputStream getOutputStream() throws IOException {
262         if (!sc.isOpen())
263             throw new SocketException("Socket is closed");
264         if (!sc.isConnected())
265             throw new SocketException("Socket is not connected");
266         if (!sc.isOutputOpen())
267             throw new SocketException("Socket output is shutdown");
268         OutputStream os = null;
269         try {
270             os = AccessController.doPrivileged(
271                 new PrivilegedExceptionAction<OutputStream>() {
272                     public OutputStream run() throws IOException {
273                         return Channels.newOutputStream(sc);
274                     }
275                 });
276         } catch (java.security.PrivilegedActionException e) {
277             throw (IOException)e.getException();
278         }
279         return os;
280     }
281 
282     private void setBooleanOption(SocketOption<Boolean> name, boolean value)
283         throws SocketException
284     {
285         try {
286             sc.setOption(name, value);
287         } catch (IOException x) {
288             Net.translateToSocketException(x);
289         }
290     }
291 
292     private void setIntOption(SocketOption<Integer> name, int value)
293         throws SocketException
294     {
295         try {
296             sc.setOption(name, value);
297         } catch (IOException x) {
298             Net.translateToSocketException(x);
299         }
300     }
301 
302     private boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException {
303         try {
304             return sc.getOption(name).booleanValue();
305         } catch (IOException x) {
306             Net.translateToSocketException(x);
307             return false;       // keep compiler happy
308         }
309     }
310 
311     private int getIntOption(SocketOption<Integer> name) throws SocketException {
312         try {
313             return sc.getOption(name).intValue();
314         } catch (IOException x) {
315             Net.translateToSocketException(x);
316             return -1;          // keep compiler happy
317         }
318     }
319 
320     public void setTcpNoDelay(boolean on) throws SocketException {
321         setBooleanOption(StandardSocketOptions.TCP_NODELAY, on);
322     }
323 
324     public boolean getTcpNoDelay() throws SocketException {
325         return getBooleanOption(StandardSocketOptions.TCP_NODELAY);
326     }
327 
328     public void setSoLinger(boolean on, int linger) throws SocketException {
329         if (!on)
330             linger = -1;
331         setIntOption(StandardSocketOptions.SO_LINGER, linger);
332     }
333 
334     public int getSoLinger() throws SocketException {
335         return getIntOption(StandardSocketOptions.SO_LINGER);
336     }
337 
338     public void sendUrgentData(int data) throws IOException {
339         synchronized (sc.blockingLock()) {
340             if (!sc.isBlocking())
341                 throw new IllegalBlockingModeException();
342             int n = sc.sendOutOfBandData((byte)data);
343             assert n == 1;
344         }
345     }
346 
347     public void setOOBInline(boolean on) throws SocketException {
348         setBooleanOption(ExtendedSocketOption.SO_OOBINLINE, on);
349     }
350 
351     public boolean getOOBInline() throws SocketException {
352         return getBooleanOption(ExtendedSocketOption.SO_OOBINLINE);
353     }
354 
355     public void setSoTimeout(int timeout) throws SocketException {
356         if (timeout < 0)
357             throw new IllegalArgumentException("timeout can't be negative");
358         this.timeout = timeout;
359     }
360 
361     public int getSoTimeout() throws SocketException {
362         return timeout;
363     }
364 
365     public void setSendBufferSize(int size) throws SocketException {
366         // size 0 valid for SocketChannel, invalid for Socket
367         if (size <= 0)
368             throw new IllegalArgumentException("Invalid send size");
369         setIntOption(StandardSocketOptions.SO_SNDBUF, size);
370     }
371 
372     public int getSendBufferSize() throws SocketException {
373         return getIntOption(StandardSocketOptions.SO_SNDBUF);
374     }
375 
376     public void setReceiveBufferSize(int size) throws SocketException {
377         // size 0 valid for SocketChannel, invalid for Socket
378         if (size <= 0)
379             throw new IllegalArgumentException("Invalid receive size");
380         setIntOption(StandardSocketOptions.SO_RCVBUF, size);
381     }
382 
383     public int getReceiveBufferSize() throws SocketException {
384         return getIntOption(StandardSocketOptions.SO_RCVBUF);
385     }
386 
387     public void setKeepAlive(boolean on) throws SocketException {
388         setBooleanOption(StandardSocketOptions.SO_KEEPALIVE, on);
389     }
390 
391     public boolean getKeepAlive() throws SocketException {
392         return getBooleanOption(StandardSocketOptions.SO_KEEPALIVE);
393     }
394 
395     public void setTrafficClass(int tc) throws SocketException {
396         setIntOption(StandardSocketOptions.IP_TOS, tc);
397     }
398 
399     public int getTrafficClass() throws SocketException {
400         return getIntOption(StandardSocketOptions.IP_TOS);
401     }
402 
403     public void setReuseAddress(boolean on) throws SocketException {
404         setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on);
405     }
406 
407     public boolean getReuseAddress() throws SocketException {
408         return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
409     }
410 
411     public void close() throws IOException {
412         sc.close();
413     }
414 
415     public void shutdownInput() throws IOException {
416         try {
417             sc.shutdownInput();
418         } catch (Exception x) {
419             Net.translateException(x);
420         }
421     }
422 
423     public void shutdownOutput() throws IOException {
424         try {
425             sc.shutdownOutput();
426         } catch (Exception x) {
427             Net.translateException(x);
428         }
429     }
430 
431     public String toString() {
432         if (sc.isConnected())
433             return "Socket[addr=" + getInetAddress() +
434                 ",port=" + getPort() +
435                 ",localport=" + getLocalPort() + "]";
436         return "Socket[unconnected]";
437     }
438 
439     public boolean isConnected() {
440         return sc.isConnected();
441     }
442 
443     public boolean isBound() {
444         return sc.localAddress() != null;
445     }
446 
447     public boolean isClosed() {
448         return !sc.isOpen();
449     }
450 
451     public boolean isInputShutdown() {
452         return !sc.isInputOpen();
453     }
454 
455     public boolean isOutputShutdown() {
456         return !sc.isOutputOpen();
457     }
458 
459 }